/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ehsunbehravesh.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 *
 * @author ehsun7b
 */
public class BlockRSA {

  public static final String RSA_ALGORITHM = "RSA";
  private int keySize;

  public BlockRSA(int keySize) {
    this.keySize = keySize;
  }

  public byte[] encryptRSA(final Key key, final byte[] data) throws IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException {
    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return cipher.doFinal(data);
  }

  public KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
    keyPairGenerator.initialize(keySize);
    return keyPairGenerator.generateKeyPair();
  }

  public byte[] decryptRSA(final Key key, final byte[] encrypted) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, key);
    return cipher.doFinal(encrypted);
  }

  public List<byte[]> splitBytes(final byte[] bytes, final int length) throws UnsupportedEncodingException, IOException {
    List<byte[]> result = new ArrayList<byte[]>();

    if (length > bytes.length) {
      result.add(bytes);
    } else {
      byte[] buffer = new byte[length];
      ByteArrayInputStream is = new ByteArrayInputStream(bytes);

      int read;
      while ((read = is.read(buffer)) > 0) {
        if (read == bytes.length) {
          result.add(bytes);
        } else {
          byte[] lastChunk = new byte[read];
          System.arraycopy(buffer, 0, lastChunk, 0, lastChunk.length);
          result.add(lastChunk);
        }
      }
    }

    return result;
  }

  public List<byte[]> splitString(final String data, final int length) throws UnsupportedEncodingException, IOException {
    byte[] bytes = data.getBytes("UTF-8");
    return splitBytes(bytes, length);
  }

  public byte[] encryptBlock(final Key key, final byte[] data) throws UnsupportedEncodingException, IOException, IllegalBlockSizeException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException {
    int blockSize = keySize / 8 - 11;

    List<byte[]> blocks = splitBytes(data, blockSize);

    int mod = data.length % blockSize;
    int size = data.length / blockSize;
    size += mod == 0 ? 0 : 1;
    byte[][] encryptedBlocks = new byte[size][];

    int i = 0;
    for (byte[] block : blocks) {
      byte[] encryptedBlock = encryptRSA(key, block);
      encryptedBlocks[i++] = encryptedBlock;
    }

    List<Byte> resultList = new ArrayList<Byte>();

    for (byte[] bytes : encryptedBlocks) {
      List<Byte> list = convertToList(bytes);
      resultList.addAll(list);
    }

    byte[] result = convertToArray(resultList);

    return result;
  }

  public byte[] decryptBlock(final Key key, final byte[] data) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
    int blockSize = keySize / 8;

    List<byte[]> blocks = splitBytes(data, blockSize);

    int mod = data.length % blockSize;
    int size = data.length / blockSize;
    size += mod == 0 ? 0 : 1;
    byte[][] decryptedBlocks = new byte[size][];

    int i = 0;
    for (byte[] block : blocks) {
      byte[] decryptedBlock = decryptRSA(key, block);
      decryptedBlocks[i++] = decryptedBlock;
    }

    List<Byte> resultList = new ArrayList<Byte>();

    for (byte[] bytes : decryptedBlocks) {
      List<Byte> list = convertToList(bytes);
      resultList.addAll(list);
    }

    byte[] result = convertToArray(resultList);

    return result;
  }

  private List<Byte> convertToList(byte[] bytes) {
    List<Byte> result = new ArrayList<Byte>();

    for (byte b : bytes) {
      result.add(b);
    }

    return result;
  }

  private byte[] convertToArray(List<Byte> list) {
    byte[] result = new byte[list.size()];

    int i = 0;
    for (Byte byte1 : list) {
      result[i++] = byte1;
    }

    return result;
  }

  /*
   public static void main(String[] args) throws UnsupportedEncodingException, IOException {
   String msg = "hell kdfh  sdkjh sdf sdfk dsسعید skdfh ksdjhf kjdsh fkdsjh fdskfkjsdf kdsjf kldsjf احسان sdkjf ssd fsdlfk jsdklfj sdklj kldsfj";
   int keySize = 512;

   BlockRSA cipher = new BlockRSA(keySize);

   byte[] bytes = null;

   bytes = msg.getBytes("UTF-8");

   KeyPair key = cipher.generateRSAKeyPair(keySize);
   byte[] encryptBlock = null;

   encryptBlock = cipher.encryptBlock(key.getPublic(), msg.getBytes("UTF-8"));


   //System.out.println(Base64Coder.encode(encryptRSA));
   System.out.println(Base64Coder.encode(encryptBlock));

   //byte[] decryptRSA = cipher.decryptRSA(key.getPrivate(), encryptRSA);
   byte[] decryptBlock = cipher.decryptBlock(key.getPrivate(), encryptBlock);

   //System.out.println(new String(decryptRSA));
   System.out.println(new String(decryptBlock));

   }*/
}
